package awesome.cpu.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JPanel;
import javax.swing.JToolTip;

import awesome.cpu.control.CentralProcessorUnit;
import awesome.cpu.structure.ALUOut;
import awesome.cpu.structure.BranchCondGates;
import awesome.cpu.structure.BusMonitor;
import awesome.cpu.structure.Component;
import awesome.cpu.structure.Control;
import awesome.cpu.structure.FlagRegister;
import awesome.cpu.structure.ImmShifter;
import awesome.cpu.structure.InstructionRegister;
import awesome.cpu.structure.Memory;
import awesome.cpu.structure.MemoryRegister;
import awesome.cpu.structure.Mux2PC;
import awesome.cpu.structure.Mux2WD1;
import awesome.cpu.structure.Mux2WR1;
import awesome.cpu.structure.MuxA;
import awesome.cpu.structure.MuxB;
import awesome.cpu.structure.MuxImm2Ex;
import awesome.cpu.structure.MuxPC2M;
import awesome.cpu.structure.OrGate;
import awesome.cpu.structure.PCRegister;
import awesome.cpu.structure.RegBShifter;
import awesome.cpu.structure.RegisterA;
import awesome.cpu.structure.RegisterB;
import awesome.cpu.structure.RegisterC;
import awesome.cpu.structure.RegisterFile;

public class OutputGraph extends JPanel {
	private static final long serialVersionUID = -3630954824203782778L;
	
	private static int animation_time = 1000;
	private int animationProgress = 0;
	private static Set<Bus> animatedBus = new HashSet<Bus>();
	
	private final static int width = 900;
	private final static int height = 540;
	
	private final static int full_row_height = 19;
	private final static int empty_row_height = 5;
	private final static int full_col_width = 37;
	private final static int empty_col_width = 20;
	
	private final static int X_GRID[] = {
		0, 20, 57, 94, 114, 151, 171, 208, 245, 265, 302, 339, 376, 413, 433,
		470, 490, 527, 547, 584, 604, 641, 661, 698, 735, 772, 809, 829, 866
	};
	private final static int Y_GRID[] = {
		0, 10, 29, 48, 67, 86, 105, 124, 143, 162, 181, 186, 191, 210, 229, 248,
		267, 286, 305, 324, 343, 362, 381, 400, 405, 424, 443, 462, 481, 500
	};
	
	final static int BRANCH_COND_GATES = 0;
	final static int OR_GATE = 1;
	final static int PC = 2;
	final static int MUX_PC2M = 3;
	final static int MEMORY = 4;
	final static int PCREG = 5;
	final static int IR = 6;
	final static int CONTROL = 7;
	final static int MUX_2WR1 = 8;
	final static int MUX_2WD1 = 9;
	final static int MR = 10;
	final static int REG_FILE = 11;
	final static int MUX_2EX = 12;
	final static int REGA = 13;
	final static int REGB = 14;
	final static int REGC = 15;
	final static int IMM_SHIFTER = 16;
	final static int REGB_SHIFTER = 17;
	final static int MUXA = 18;
	final static int MUXB = 19;
	final static int FLAG_REG = 20;
	final static int ALU = 21;
	final static int MUX_2PC = 22;
	final static int ALU_OUT = 23;
	private final static int CONST = 24;
	
	private final static int N_COMPONENT = 24;
	
	private final static int X_COMPONENT[] = {
		1, 1, 1, 4, 6, 7, 9, 10, 12, 12, 9, 14, 14, 16, 16, 16, 16,
		18, 20, 20, 22, 22, 27, 27
	};
	private final static int Y_COMPONENT[] = {
		2, 7, 13, 13, 12, 27, 12, 1, 14, 18, 24, 12, 25, 14, 16, 18, 27,
		16, 13, 18, 1, 15, 5, 17
	};
	private final static int W_COMPONENT[] = {
		1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 4, 4, 1, 1
	};
	private final static int H_COMPONENT[] = {
		2, 1, 1, 2, 6, 1, 9, 9, 3, 5, 1, 9, 2, 1, 1, 1, 2,
		2, 2, 5, 1, 5, 3, 1
	};
	
	private final static int COLOR_COMPONENT[] = {
		0xF4FA58, 0xFFBF00, 0x4000FF, 0xEE6AA7, 0x8A2908, 0x0174DF,
		0x088A08, 0xB404AE, 0xEE6AA7, 0xEE6AA7, 0xDF3A01, 0x08088A,
		0xEE6AA7, 0x0404B4, 0x0404B4, 0x0404B4, 0x74DF00, 0x74DF00,
		0xEE6AA7, 0xEE6AA7, 0xDF0101, 0xBE81F7, 0xEE6AA7, 0xF781F3
	};
	
	private final Bus BUS[] = {
		new Bus(CONTROL, Control.KEY_PC_WRITE_CONDITION,
				BRANCH_COND_GATES, BranchCondGates.KEY_PCWRITE_COND),
		new Bus(CONTROL, Control.KEY_PC_WRITE, OR_GATE, OrGate.KEY_PCWRITE),
		new Bus(CONTROL, Control.KEY_I_OR_D, MUX_PC2M, MuxPC2M.KEY_IORD),
		new Bus(CONTROL, Control.KEY_MEM_READ, MEMORY, Memory.KEY_MEM_READ),
		new Bus(CONTROL, Control.KEY_MEM_WRITE, MEMORY, Memory.KEY_MEM_WRITE),
		new Bus(CONTROL, Control.KEY_MEM_TO_REG, MUX_2WD1, Mux2WD1.KEY_MEMTOREG),
		new Bus(CONTROL, Control.KEY_IR_WRITE, IR, InstructionRegister.KEY_IRWRITE_ENABLE),
		new Bus(CONTROL, Control.KEY_IMM_SRC, MUX_2EX, MuxImm2Ex.KEY_IMM_SRC),
		new Bus(CONTROL, Control.KEY_PCREG_WRITE, PCREG, PCRegister.KEY_PCREG_WRITE),
		new Bus(CONTROL, Control.KEY_FLAG_WRITE, FLAG_REG, FlagRegister.KEY_FLAG_WRITE),
		new Bus(CONTROL, Control.KEY_PC_SOURCE, MUX_2PC, Mux2PC.KEY_PC_SRC),
		new Bus(CONTROL, Control.KEY_ALU_SRC_A, MUXA, MuxA.KEY_ALU_SRCA),
		new Bus(CONTROL, Control.KEY_ALU_SRC_B, MUXB, MuxB.KEY_ALU_SRCB),
		new Bus(CONTROL, Control.KEY_ALU_OP, ALU,
				awesome.cpu.structure.ALU.KEY_CONTROL_INPUT),
		new Bus(CONTROL, Control.KEY_REG_WRITE, REG_FILE, RegisterFile.KEY_WRITE_ENABLE),
		new Bus(CONTROL, Control.KEY_REG_DST, MUX_2WR1, Mux2WR1.KEY_REG_DST),
		new Bus(BRANCH_COND_GATES, BranchCondGates.KEY_RESULT,
				OR_GATE, OrGate.KEY_CONDITION_MET),
		new Bus(OR_GATE, OrGate.KEY_RESULT, PC,
				awesome.cpu.structure.PC.KEY_PC_WRITE_ENABLE),
		new Bus(PC, awesome.cpu.structure.PC.KEY_PC_OUT,
				MUX_PC2M, MuxPC2M.KEY_PC),
		new Bus(PC, awesome.cpu.structure.PC.KEY_PC_OUT,
				MUXA, MuxA.KEY_PC),
		new Bus(PC, awesome.cpu.structure.PC.KEY_PC_OUT,
				MUX_2WD1, Mux2WD1.KEY_PC),
		new Bus(PC, awesome.cpu.structure.PC.KEY_PC_OUT,
				PCREG, PCRegister.KEY_PCREG_IN),
		new Bus(MUX_PC2M, MuxPC2M.KEY_SELECTED, MEMORY, Memory.KEY_MEM_ADDR),
		new Bus(MEMORY, Memory.KEY_READ_DATA, 
				IR, InstructionRegister.KEY_INSTRUCTION_INPUT),
		new Bus(MEMORY, Memory.KEY_READ_DATA,
				MR, MemoryRegister.KEY_MEM_DATA),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_31_24,
				CONTROL, Control.KEY_OPERATION),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_23_19,
				REG_FILE, RegisterFile.KEY_READ_REG1),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_18_14,
				MUX_2WR1, Mux2WR1.KEY_RD1),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_18_14,
				REG_FILE, RegisterFile.KEY_READ_REG3),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_13_9,
				REG_FILE, RegisterFile.KEY_WRITE_REG2),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_13_9,
				IMM_SHIFTER, ImmShifter.KEY_IMM5),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_13_9,
				REGB_SHIFTER, RegBShifter.KEY_SHIFT_CNT),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_8_5,
				CONTROL, Control.KEY_FUNCTION),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_4_0,
				MUX_2WR1, Mux2WR1.KEY_RS2),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_4_0,
				REG_FILE, RegisterFile.KEY_READ_REG2),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_23_0,
				MUX_2EX, MuxImm2Ex.KEY_IMM24),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_13_0,
				MUX_2EX, MuxImm2Ex.KEY_IMM14),
		new Bus(IR, InstructionRegister.KEY_INSTRUCTION_8_0,
				IMM_SHIFTER, ImmShifter.KEY_IMM9),
		new Bus(MUX_2WR1, Mux2WR1.KEY_SELECTED, REG_FILE, RegisterFile.KEY_WRITE_REG1),
		new Bus(MUX_2WD1, Mux2WD1.KEY_SELECTED, REG_FILE, RegisterFile.KEY_WRITE_DATA1),
		new Bus(MR, MemoryRegister.KEY_MEM_REG, MUX_2WD1, Mux2WD1.KEY_MR),
		new Bus(PCREG, PCRegister.KEY_PCREG_OUT, MUX_2WD1, Mux2WD1.KEY_PCREG),
		new Bus(REG_FILE, RegisterFile.KEY_READ_DATA1, REGA, RegisterA.KEY_REGA_IN),
		new Bus(REG_FILE, RegisterFile.KEY_READ_DATA2, REGB, RegisterB.KEY_REGB_IN),
		new Bus(REG_FILE, RegisterFile.KEY_READ_DATA3, REGC, RegisterC.KEY_REGC_IN),
		new Bus(REGA, RegisterA.KEY_REGA_OUT, MUXA, MuxA.KEY_REGA),
		new Bus(REGB, RegisterB.KEY_REGB_OUT, REGB_SHIFTER, RegBShifter.KEY_REGB),
		new Bus(REGB, RegisterB.KEY_REGB_OUT, MUXB, MuxB.KEY_REGB_ORIG),
		new Bus(REGC, RegisterC.KEY_REGC_OUT, MEMORY, Memory.KEY_WRITE_DATA),
		new Bus(REGB_SHIFTER, RegBShifter.KEY_RESULT, MUX_2PC, Mux2PC.KEY_PC_JUMP),
		new Bus(REGB_SHIFTER, RegBShifter.KEY_RESULT, MUXB, MuxB.KEY_REGB_SHIFTED),
		new Bus(IMM_SHIFTER, ImmShifter.KEY_RESULT, MUXB, MuxB.KEY_IMM_SHIFTED),
		new Bus(MUX_2EX, MuxImm2Ex.KEY_SELECTED, MUXB, MuxB.KEY_IMM_EXTENDED),
		new Bus(MUXA, MuxA.KEY_SELECTED, ALU, awesome.cpu.structure.ALU.KEY_INPUT1),
		new Bus(MUXB, MuxB.KEY_SELECTED, ALU, awesome.cpu.structure.ALU.KEY_INPUT2),
		new Bus(MUXB, MuxB.KEY_SELECTED, MUX_2WD1, Mux2WD1.KEY_MOV_SRC),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_ALU_RESULT_31_0,
				ALU_OUT, ALUOut.KEY_ALU_RESULT_LO),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_ALU_RESULT_63_32,
				ALU_OUT, ALUOut.KEY_ALU_RESULT_HI),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_N, FLAG_REG, FlagRegister.KEY_N),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_Z, FLAG_REG, FlagRegister.KEY_Z),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_C, FLAG_REG, FlagRegister.KEY_C),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_V, FLAG_REG, FlagRegister.KEY_V),
		new Bus(ALU, awesome.cpu.structure.ALU.KEY_ALU_RESULT_31_0,
				MUX_2PC, Mux2PC.KEY_PC_NEXT),
		new Bus(ALU_OUT, ALUOut.KEY_ALU_OUT_HI, REG_FILE, RegisterFile.KEY_WRITE_DATA2),
		new Bus(ALU_OUT, ALUOut.KEY_ALU_OUT_LO, MUX_2WD1, Mux2WD1.KEY_ALUOUT),
		new Bus(ALU_OUT, ALUOut.KEY_ALU_OUT_LO, MUX_2PC, Mux2PC.KEY_PC_BRANCH),
		new Bus(ALU_OUT, ALUOut.KEY_ALU_OUT_LO, MUX_PC2M, MuxPC2M.KEY_ALUOUT),
		new Bus(FLAG_REG, FlagRegister.KEY_FLAG_ALL,
				BRANCH_COND_GATES, BranchCondGates.KEY_FLAG),
		new Bus(MUX_2PC, Mux2PC.KEY_SELECTED, PC, awesome.cpu.structure.PC.KEY_PC_IN),
		new Bus(CONST, 30, MUX_2WR1, 2),
		new Bus(CONST, 4, MUXB, 1),
	};
	
	private final static String NAME_COMPONENT[] = {
		"Branch Condition Gates",
		"Or Gate",
		"Program Counter",
		"Mux",
		"Memory",
		"PC Register",
		"Instruction Register",
		"Control",
		"Mux",
		"Mux",
		"Memory Register",
		"Register File",
		"Mux",
		"Register A",
		"Register B",
		"Register C",
		"Shifter",
		"Shifter",
		"Mux",
		"Mux",
		"Flag Register",
		"Arithmatic Logic Unit",
		"Mux",
		"ALU Out"
	};
	
	OutputGraph() {
		super();
		Dimension d = new Dimension(width, height);
		this.setPreferredSize(d);
		this.setMaximumSize(d);
		this.setMinimumSize(d);
		this.setBackground(Color.white);
		this.setLayout(null);
		
		setToolTips();
	}
	
	private void setToolTips() {
		for (int i = 0; i < N_COMPONENT; i++) {
			final int component = i;
			JPanel p = new JPanel() {
				private static final long serialVersionUID = -8510877979966177721L;

				@Override
				public JToolTip createToolTip() {
					return new GraphToolTip(component);
				}
				@Override
				public void paint(Graphics g) {
				}
			};
			p.setOpaque(true);
			this.add(p);
			int left = X_GRID[X_COMPONENT[i]];
			int top = Y_GRID[Y_COMPONENT[i]];
			int right = X_GRID[X_COMPONENT[i]+W_COMPONENT[i]];
			int bottom = Y_GRID[Y_COMPONENT[i]+H_COMPONENT[i]]; 
			p.setBounds(left, top, right-left, bottom-top);
			//p.setLocation(left, top);
			p.setToolTipText(NAME_COMPONENT[i]);
		}
	}
	
	static Dimension getToolTipSize(int component) {
		if (component == -1) return new Dimension(0, 0);
		return new Dimension(X_GRID[X_COMPONENT[component]+W_COMPONENT[component]]
				-X_GRID[X_COMPONENT[component]],
				Y_GRID[Y_COMPONENT[component]+H_COMPONENT[component]]
				-Y_GRID[Y_COMPONENT[component]]);
	}
	
	void setAnimationTime(int length) {
		animation_time = length;
	}
	
	static void clearAnimatedBus() {
		synchronized (animatedBus) {
			animatedBus.clear();
		}
	}
	
	static void putAnimatedBus(Component cfrom, int ifrom, Component cto, int ito) {
		synchronized (animatedBus) {
			animatedBus.add(new Bus(getCode(cfrom), ifrom, getCode(cto), ito));
		}
	}
	
	private static int getCode(Component c) {
		if (c instanceof BranchCondGates) return BRANCH_COND_GATES;
		if (c instanceof OrGate) return OR_GATE;
		if (c instanceof awesome.cpu.structure.PC) return PC;
		if (c instanceof MuxPC2M) return MUX_PC2M;
		if (c instanceof Memory) return MEMORY;
		if (c instanceof PCRegister) return PCREG;
		if (c instanceof InstructionRegister) return IR;
		if (c instanceof Control) return CONTROL;
		if (c instanceof Mux2WR1) return MUX_2WR1;
		if (c instanceof Mux2WD1) return MUX_2WD1;
		if (c instanceof MemoryRegister) return MR;
		if (c instanceof RegisterFile) return REG_FILE;
		if (c instanceof MuxImm2Ex) return MUX_2EX;
		if (c instanceof RegisterA) return REGA;
		if (c instanceof RegisterB) return REGB;
		if (c instanceof RegisterC) return REGC;
		if (c instanceof ImmShifter) return IMM_SHIFTER;
		if (c instanceof RegBShifter) return REGB_SHIFTER;
		if (c instanceof MuxA) return MUXA;
		if (c instanceof MuxB) return MUXB;
		if (c instanceof FlagRegister) return FLAG_REG;
		if (c instanceof awesome.cpu.structure.ALU) return ALU;
		if (c instanceof Mux2PC) return MUX_2PC;
		if (c instanceof ALUOut) return ALU_OUT;
		return -1;
	}
	
	public void startAnimation() {
		new Thread() {
			@Override
			public void run() {
				while (CentralProcessorUnit.getInstance().isRunning()) {
					synchronized (BusMonitor.getInstance()){
						try {
							BusMonitor.getInstance().wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					//if (!CentralProcessorUnit.getInstance().isRunning()) break;
					execAnimation();
				}
			}
		}.start();
	}
	
	public void execAnimation() {
		new Thread() {
			@Override
			public void run() {
				for (animationProgress = 1; animationProgress <= 100; animationProgress++) {
					try {
						Thread.sleep(animation_time/100);
						OutputGraph.this.updateUI();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				animationProgress = 0;
			}
		}.start();
	}
	
	@Override
	public void paint(Graphics g) {
		super.paint(g);
		
		for (int i = 0; i < N_COMPONENT; i++) {
			drawComponent(g, i);
		}
		
		for (Bus bus : BUS) {
			if (bus.cfrom == CONST) {
				g.setColor(Color.black);
				int endX = X_GRID[X_COMPONENT[bus.cto]];
				int endY = Y_GRID[Y_COMPONENT[bus.cto]]+full_row_height*(bus.ito*2+1)/2;
				int startX = endX-5;
				int startY = endY;
				g.drawLine(startX, startY, endX, endY);
				int tmp = bus.ifrom > 9 ? 16 : 7;
				g.drawString(String.valueOf(bus.ifrom), startX-tmp, startY+g.getFont().getSize()/2);
			} else {	
				g.setColor(new Color(COLOR_COMPONENT[bus.cfrom]));
				drawBusPath(g, bus.getBusPath(), false);
			}
		}
		
		if (animationProgress > 0) {
			synchronized (animatedBus) {
				for (Bus bus : animatedBus) {
					g.setColor(new Color(COLOR_COMPONENT[bus.cfrom]));
					BusPath p = bus.getBusPath().getProgressed(animationProgress);
					drawBusPath(g, p, true);
				}
			}
		}
	}
	
	private void drawComponent(Graphics g, int component) {
		int left = X_GRID[X_COMPONENT[component]];
		int top = Y_GRID[Y_COMPONENT[component]];
		int right = X_GRID[X_COMPONENT[component]+W_COMPONENT[component]];
		int bottom = Y_GRID[Y_COMPONENT[component]+H_COMPONENT[component]];
		g.setColor(new Color(COLOR_COMPONENT[component]));
		g.fillRect(left, top, right-left, bottom-top);
	}
	
	private void drawBusPath(Graphics g, BusPath path, boolean emphasized) {
		for (int i = 0; i < path.points.size()-1; i++) {
			Point p1 = path.points.get(i);
			Point p2 = path.points.get(i+1);
			if (!emphasized) {
				g.drawLine(p1.x, p1.y, p2.x, p2.y);
			} else {
				if (p1.x == p2.x) { // vertical line
					g.drawLine(p1.x-1, p1.y, p2.x-1, p2.y);
					g.drawLine(p1.x+1, p1.y, p2.x+1, p2.y);
				} else { // horizontal line
					g.drawLine(p1.x, p1.y-1, p2.x, p2.y-1);
					g.drawLine(p1.x, p1.y+1, p2.x, p2.y+1);
				}
			}
		}
		for (Point node : path.nodes) {
			g.fillOval(node.x-2, node.y-2, 4, 4);
		}
	}
	
	static Map<String, Integer> getNameColorMap() {
		Map<String, Integer> map = new HashMap<String, Integer>();
		map.put("Control", COLOR_COMPONENT[CONTROL]);
		map.put("Instruction Register", COLOR_COMPONENT[IR]);
		map.put("Register File", COLOR_COMPONENT[REG_FILE]);
		map.put("ALU", COLOR_COMPONENT[ALU]);
		map.put("ALU Out", COLOR_COMPONENT[ALU_OUT]);
		map.put("Memory", COLOR_COMPONENT[MEMORY]);
		map.put("Memory Register", COLOR_COMPONENT[MR]);
		map.put("PC", COLOR_COMPONENT[PC]);
		map.put("PC Register", COLOR_COMPONENT[PCREG]);
		map.put("Mux", COLOR_COMPONENT[MUXA]);
		map.put("Shifter", COLOR_COMPONENT[REGB_SHIFTER]);
		map.put("Register", COLOR_COMPONENT[REGA]);
		map.put("BranchConditionGates", COLOR_COMPONENT[BRANCH_COND_GATES]);
		map.put("OrGate", COLOR_COMPONENT[OR_GATE]);
		map.put("Flag Register", COLOR_COMPONENT[FLAG_REG]);
		return map;
	}
	
	private static class Bus {
		int cfrom, ifrom, cto, ito;
		private BusPath path = null;
		
		Bus(int cfrom, int ifrom, int cto, int ito) {
			this.cfrom = cfrom;
			this.ifrom = ifrom;
			this.cto = cto;
			this.ito = ito;
		}
		
		BusPath getBusPath() {
			if (path != null) return path;
			int startX = 0, startY = 0, endX = 0, endY = 0,
					interX = 0, interY = 0, nodeX = 0, nodeY = 0, interX1 = 0;
				switch (cfrom) {
				case CONTROL:
					switch (ifrom) {
					case Control.KEY_PC_WRITE_CONDITION:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height/2;
						// dest: BranchCondGates.PCWriteCond
						endX = X_GRID[X_COMPONENT[BRANCH_COND_GATES]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[BRANCH_COND_GATES]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_PC_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*3/2;
						// dest: OrGate.PCWrite
						endX = X_GRID[X_COMPONENT[OR_GATE]+W_COMPONENT[OR_GATE]]-full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[OR_GATE]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_I_OR_D:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*5/2;
						// dest: MuxPC2M.IorD
						endX = X_GRID[X_COMPONENT[MUX_PC2M]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUX_PC2M]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_MEM_READ:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*7/2;
						// dest: Memory.MemRead
						endX = X_GRID[X_COMPONENT[MEMORY]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MEMORY]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_MEM_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*9/2;
						// dest: Memory.MemWrite
						endX = X_GRID[X_COMPONENT[MEMORY]]+full_col_width*3/2;
						endY = Y_GRID[Y_COMPONENT[MEMORY]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_MEM_TO_REG:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*11/2;
						// dest: Mux2WD1.MemToReg
						endX = X_GRID[X_COMPONENT[MUX_2WD1]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUX_2WD1]+H_COMPONENT[MUX_2WD1]];
						// inter-points:
						interX = X_GRID[X_COMPONENT[IR]]-empty_col_width/5;
						interY = endY+empty_row_height/3;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(endX, interY);
						path.append(endX, endY);
						return path;
					case Control.KEY_IR_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*13/2;
						// dest: IR.IRWrite 
						endX = X_GRID[X_COMPONENT[IR]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[IR]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_IMM_SRC:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*15/2;
						// dest: MuxImm2Ex.ImmSrc
						endX = X_GRID[X_COMPONENT[MUX_2EX]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUX_2EX]+H_COMPONENT[MUX_2EX]];
						interX = X_GRID[X_COMPONENT[IR]]-empty_col_width/3;
						interY = endY+empty_row_height/3;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(endX, interY);
						path.append(endX, endY);
						return path;
					case Control.KEY_PCREG_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*17/2;
						// dest: PCReg.PCRegWrite
						endX = X_GRID[X_COMPONENT[PCREG]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[PCREG]];
						interX = X_GRID[X_COMPONENT[MEMORY]+W_COMPONENT[MEMORY]]+empty_col_width/2;
						interY = endY-empty_row_height;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(endX, interY);
						path.append(endX, endY);
						return path;
					case Control.KEY_FLAG_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height/2;
						// dest: FlagReg.FlagWrite
						endX = X_GRID[X_COMPONENT[FLAG_REG]];
						endY = Y_GRID[Y_COMPONENT[FLAG_REG]]+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_PC_SOURCE:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*5/2;
						// dest: Mux2PC.PCSrc
						endX = X_GRID[X_COMPONENT[MUX_2PC]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUX_2PC]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_ALU_SRC_A:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*7/2;
						// dest: MuxA.ALUSrcA
						endX = X_GRID[X_COMPONENT[MUXA]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUXA]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_ALU_SRC_B:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*9/2;
						// dest: MuxB.ALUSrcB
						endX = X_GRID[X_COMPONENT[MUXB]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUXB]];
						// inter-points:
						interX = X_GRID[X_COMPONENT[MUXB]]-empty_col_width/3;
						interY = endY-full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(endX, interY);
						path.append(endX, endY);
						return path;
					case Control.KEY_ALU_OP:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*11/2;
						// dest: ALU.controlInput
						endX = X_GRID[X_COMPONENT[ALU]+W_COMPONENT[ALU]]-full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[ALU]+H_COMPONENT[ALU]];
						interX = X_GRID[X_COMPONENT[ALU]+W_COMPONENT[ALU]]+empty_col_width/3;
						interY = endY+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(endX, interY);
						path.append(endX, endY);
						return path;
					case Control.KEY_REG_WRITE:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*13/2;
						// dest: RegFile.RegWrite
						endX = X_GRID[X_COMPONENT[REG_FILE]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[REG_FILE]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case Control.KEY_REG_DST:
						startX = X_GRID[X_COMPONENT[CONTROL]+W_COMPONENT[CONTROL]];
						startY = Y_GRID[Y_COMPONENT[CONTROL]]+full_row_height*15/2;
						// dest: Mux2WR1.RegDst
						endX = X_GRID[X_COMPONENT[MUX_2WR1]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[MUX_2WR1]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					}
					break;
				case BRANCH_COND_GATES:
					// BranchCondGates.result => OrGate.input1
					startX = X_GRID[X_COMPONENT[BRANCH_COND_GATES]]+full_col_width/2;
					startY = Y_GRID[Y_COMPONENT[BRANCH_COND_GATES]+H_COMPONENT[BRANCH_COND_GATES]];
					endX = X_GRID[X_COMPONENT[OR_GATE]]+full_col_width/2;
					endY = Y_GRID[Y_COMPONENT[OR_GATE]];
					path = new BusPath(startX, startY);
					path.append(endX, endY);
					return path;
				case OR_GATE:
					// OrGate.result => PC.PCWriteEnable
					startX = X_GRID[X_COMPONENT[OR_GATE]]+full_col_width;
					startY = Y_GRID[Y_COMPONENT[OR_GATE]+H_COMPONENT[OR_GATE]];
					endX = X_GRID[X_COMPONENT[PC]]+full_col_width/2;
					endY = Y_GRID[Y_COMPONENT[PC]];
					interY = (startY+endY)/2;
					path = new BusPath(startX, startY);
					path.append(startX, interY);
					path.append(endX, interY);
					path.append(endX, endY);
					return path;
				case PC:
					startX = X_GRID[X_COMPONENT[PC]+W_COMPONENT[PC]];
					startY = Y_GRID[Y_COMPONENT[PC]]+full_row_height/2;
					nodeX = X_GRID[X_COMPONENT[PC]+W_COMPONENT[PC]]+full_col_width/2;
					nodeY = startY;
					path = new BusPath(startX, startY);
					path.append(nodeX, nodeY);
					path.markNode(nodeX, nodeY);
					switch (cto) {
					case MUX_PC2M:
						// PC.out => MuxPC2M.input0
						endX = X_GRID[X_COMPONENT[MUX_PC2M]];
						endY = Y_GRID[Y_COMPONENT[MUX_PC2M]]+full_row_height/2;
						path.append(endX, endY);
						return path;
					case MUX_2WD1:
						// PC.out => Mux2WD1.input2
						endX = X_GRID[X_COMPONENT[MUX_2WD1]];
						endY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height*5/2;
						interX = endX-full_col_width/3;
						interY = Y_GRID[Y_COMPONENT[IR]+H_COMPONENT[IR]]+full_row_height/2;
						path.append(nodeX, interY);
						path.append(interX, interY);
						path.append(interX, endY);
						path.append(endX, endY);
						path.markNode(nodeX, interY);
						return path;
					case MUXA:
						// PC.out => MuxA.input0
						endX = X_GRID[X_COMPONENT[MUXA]];
						endY = Y_GRID[Y_COMPONENT[MUXA]]+full_row_height/2;
						interX = endX-empty_col_width/2;
						interY = Y_GRID[Y_COMPONENT[IR]]-empty_row_height;
						path.append(nodeX, interY);
						path.append(interX, interY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					case PCREG:
						// PC.out => PCReg.in
						endX = X_GRID[X_COMPONENT[PCREG]];
						endY = Y_GRID[Y_COMPONENT[PCREG]]+full_row_height/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case MUX_PC2M:
					// MuxPC2M.out => Memory.Address
					startX = X_GRID[X_COMPONENT[MUX_PC2M]+W_COMPONENT[MUX_PC2M]];
					startY = Y_GRID[Y_COMPONENT[MUX_PC2M]]+full_row_height;
					endX = X_GRID[X_COMPONENT[MEMORY]];
					endY = startY;
					path = new BusPath(startX, startY);
					path.append(endX, endY);
					return path;
				case MEMORY:
					startX = X_GRID[X_COMPONENT[MEMORY]+W_COMPONENT[MEMORY]];
					startY = Y_GRID[Y_COMPONENT[MEMORY]]+full_row_height*3;
					nodeX = startX+empty_col_width/4;
					nodeY = startY;
					path = new BusPath(startX, startY);
					path.append(nodeX, nodeY);
					path.markNode(nodeX, nodeY);
					switch (cto) {
					case IR:
						endX = X_GRID[X_COMPONENT[IR]];
						endY = nodeY;
						path.append(endX, endY);
						return path;
					case MR:
						endX = X_GRID[X_COMPONENT[MR]];
						endY = Y_GRID[Y_COMPONENT[MR]]+full_row_height/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case IR:
					startX = X_GRID[X_COMPONENT[IR]+W_COMPONENT[IR]];
					switch (ifrom) {
					case InstructionRegister.KEY_INSTRUCTION_31_24:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height/2;
						endX = X_GRID[X_COMPONENT[CONTROL]]+full_col_width/2;
						endY = Y_GRID[Y_COMPONENT[CONTROL]+H_COMPONENT[CONTROL]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case InstructionRegister.KEY_INSTRUCTION_8_5:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*3/2;
						endX = X_GRID[X_COMPONENT[CONTROL]]+full_col_width*3/2;
						endY = Y_GRID[Y_COMPONENT[CONTROL]+H_COMPONENT[CONTROL]];
						path = new BusPath(startX, startY);
						path.append(endX, startY);
						path.append(endX, endY);
						return path;
					case InstructionRegister.KEY_INSTRUCTION_23_19:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*5/2;
						endX = X_GRID[X_COMPONENT[REG_FILE]];
						endY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height/2;
						interX = X_GRID[X_COMPONENT[MUX_2WR1]]-full_col_width;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					case InstructionRegister.KEY_INSTRUCTION_4_0:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*7/2;
						interX = X_GRID[X_COMPONENT[MUX_2WR1]]-full_col_width*6/7;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						nodeX = X_GRID[X_COMPONENT[MUX_2WR1]]-full_col_width*3/7;
						nodeY = Y_GRID[Y_COMPONENT[MUX_2WR1]]+full_row_height/2;
						path.append(interX, nodeY);
						path.append(nodeX, nodeY);
						path.markNode(nodeX, nodeY);
						switch (cto) {
						case MUX_2WR1:
							// IR.rs2 => Mux2WR1.input0
							endX = X_GRID[X_COMPONENT[MUX_2WR1]];
							endY = nodeY;
							path.append(endX, endY);
							return path;
						case REG_FILE:
							// IR.rs2 => RegFile.ReadReg2
							endX = X_GRID[X_COMPONENT[REG_FILE]];
							endY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*3/2;
							path.append(nodeX, endY);
							path.append(endX, endY);
							return path;
						}
						break;
					case InstructionRegister.KEY_INSTRUCTION_18_14:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*9/2;
						nodeX = X_GRID[X_COMPONENT[MUX_2WR1]]-full_col_width*5/7;
						nodeY = startY;
						path = new BusPath(startX, startY);
						path.append(nodeX, nodeY);
						path.append(nodeX, nodeY);
						endY = Y_GRID[Y_COMPONENT[MUX_2WR1]]+full_row_height*3/2;
						switch (cto) {
						case MUX_2WR1:
							// IR.rd1 => Mux2WR1.input1
							endX = X_GRID[X_COMPONENT[MUX_2WR1]];
							endY = Y_GRID[Y_COMPONENT[MUX_2WR1]]+full_row_height*3/2;
							path.append(nodeX, endY);
							path.append(endX, endY);
							return path;
						case REG_FILE:
							// IR.rd1 => RegFile.ReadReg3
							endX = X_GRID[X_COMPONENT[REG_FILE]];
							endY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*5/2;
							interX = endX-empty_col_width*2/3;
							interY = Y_GRID[Y_COMPONENT[MUX_2WR1]+H_COMPONENT[MUX_2WR1]]
									+full_row_height/4;
							path.append(nodeX, interY);
							path.append(interX, interY);
							path.append(interX, endY);
							path.append(endX, endY);
							return path;
						}
						break;
					case InstructionRegister.KEY_INSTRUCTION_13_9:
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*11/2;
						nodeX = startX+full_col_width;
						nodeY = startY;
						path = new BusPath(startX, startY);
						path.append(nodeX, nodeY);
						path.markNode(nodeX, nodeY);
						switch (cto) {
						case REG_FILE:
							// IR.rd2 => RegFile.WriteReg2
							endX = X_GRID[X_COMPONENT[REG_FILE]];
							endY = startY+full_row_height;
							interX = endX-empty_col_width*2/3;
							path.append(interX, nodeY);
							path.append(interX, endY);
							path.append(endX, endY);
							return path;
						case REGB_SHIFTER:
							// IR.imm5 => RegBShifter.ShiftCnt
							endX = X_GRID[X_COMPONENT[REGB_SHIFTER]];
							endY = Y_GRID[Y_COMPONENT[REGB_SHIFTER]]+full_row_height*3/2;
							interY = Y_GRID[Y_COMPONENT[MR]];
							interX = endX-empty_col_width/3;
							path.append(nodeX, interY);
							path.append(interX, interY);
							path.append(interX, endY);
							path.append(endX, endY);
							path.markNode(nodeX, interY);
							return path;
						case IMM_SHIFTER:
							// IR.imm5 => ImmShifter.imm5
							endX = X_GRID[X_COMPONENT[IMM_SHIFTER]];
							endY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]]+full_row_height*3/2;
							path.append(nodeX, endY);
							path.append(endX, endY);
							return path;
						}
						break;
					case InstructionRegister.KEY_INSTRUCTION_23_0:
						// IR.imm24 => MuxImm2Ex.imm24
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*13/2;
						endX = X_GRID[X_COMPONENT[MUX_2EX]];
						endY = Y_GRID[Y_COMPONENT[MUX_2EX]]+full_row_height/2;
						interX = startX+full_col_width*3/4;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					case InstructionRegister.KEY_INSTRUCTION_13_0:
						// IR.imm14 => MuxImm2Ex.imm14
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*15/2;
						endX = X_GRID[X_COMPONENT[MUX_2EX]];
						endY = Y_GRID[Y_COMPONENT[MUX_2EX]]+full_row_height*3/2;
						interX = startX+full_col_width/2;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					case InstructionRegister.KEY_INSTRUCTION_8_0:
						// IR.imm9 => ImmShifter.imm9
						startY = Y_GRID[Y_COMPONENT[IR]]+full_row_height*17/2;
						endX = X_GRID[X_COMPONENT[IMM_SHIFTER]];
						endY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]]+full_row_height/2;
						interX = startX+full_col_width/4;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case MUX_2WR1:
					startX = X_GRID[X_COMPONENT[MUX_2WR1]+W_COMPONENT[MUX_2WR1]];
					startY = Y_GRID[Y_COMPONENT[MUX_2WR1]]+full_row_height*3/2;
					endX = X_GRID[X_COMPONENT[REG_FILE]];
					endY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*11/2;
					interX = startX+empty_col_width*2/3;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case MUX_2WD1:
					startX = X_GRID[X_COMPONENT[MUX_2WD1]+W_COMPONENT[MUX_2WD1]];
					startY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height*5/2;
					endX = X_GRID[X_COMPONENT[REG_FILE]];
					endY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*15/2;
					interX = startX+empty_col_width/3;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case MR:
					startX = X_GRID[X_COMPONENT[MR]+W_COMPONENT[MR]];
					startY = Y_GRID[Y_COMPONENT[MR]]+full_row_height/2;
					endX = X_GRID[X_COMPONENT[MUX_2WD1]];
					endY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height*3/2;
					interX = endX-full_col_width/2;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case PCREG:
					startX = X_GRID[X_COMPONENT[PCREG]+W_COMPONENT[PCREG]];
					startY = Y_GRID[Y_COMPONENT[PCREG]]+full_row_height/2;
					endX = X_GRID[X_COMPONENT[MUX_2WD1]];
					endY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height*9/2;
					interX1 = X_GRID[X_COMPONENT[IR]+W_COMPONENT[IR]];
					interX = endX-full_col_width/3;
					interY = startY+empty_row_height;
					path = new BusPath(startX, startY);
					path.append(interX1, startY);
					path.append(interX1, interY);
					path.append(interX, interY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case REG_FILE:
					startX = X_GRID[X_COMPONENT[REG_FILE]+W_COMPONENT[REG_FILE]];
					switch (ifrom) {
					case RegisterFile.KEY_READ_DATA1:
						// RegFile.ReadData1 => RegA
						startY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*5/2;
						endX = X_GRID[X_COMPONENT[REGA]];
						endY = Y_GRID[Y_COMPONENT[REGA]]+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(endX, endY);
						return path;
					case RegisterFile.KEY_READ_DATA2:
						// RegFile.ReadData2 => RegB
						startY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*9/2;
						endX = X_GRID[X_COMPONENT[REGB]];
						endY = Y_GRID[Y_COMPONENT[REGB]]+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(endX, endY);
						return path;
					case RegisterFile.KEY_READ_DATA3:
						// RegFile.ReadData3 => RegC
						startY = Y_GRID[Y_COMPONENT[REG_FILE]]+full_row_height*13/2;
						endX = X_GRID[X_COMPONENT[REGC]];
						endY = Y_GRID[Y_COMPONENT[REGC]]+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(endX, endY);
						return path;
					}
					break;
				case REGA:
					// RegA.out => MuxA.input1
					startX = X_GRID[X_COMPONENT[REGA]+W_COMPONENT[REGA]];
					startY = Y_GRID[Y_COMPONENT[REGA]]+full_row_height/2;
					endX = X_GRID[X_COMPONENT[MUXA]];
					endY = Y_GRID[Y_COMPONENT[MUXA]]+full_row_height*3/2;
					path = new BusPath(startX, startY);
					path.append(endX, endY);
					return path;
				case REGB:
					startX = X_GRID[X_COMPONENT[REGB]+W_COMPONENT[REGB]];
					startY = Y_GRID[Y_COMPONENT[REGB]]+full_row_height/2;
					nodeX = startX+empty_col_width/2;
					nodeY = startY;
					path = new BusPath(startX, startY);
					path.append(nodeX, nodeY);
					path.markNode(nodeX, nodeY);
					switch (cto) {
					case REGB_SHIFTER:
						// RegB.out => RegBShifter.RegB
						endX = X_GRID[X_COMPONENT[REGB_SHIFTER]];
						endY = Y_GRID[Y_COMPONENT[REGB_SHIFTER]]+full_row_height/2;
						path.append(endX, endY);
						return path;
					case MUXB:
						// RegB.out => MuxB.input4
						endX = X_GRID[X_COMPONENT[MUXB]];
						endY = Y_GRID[Y_COMPONENT[MUXB]]+full_row_height*9/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case REGC:
					// RegC.out => Memory.WriteData
					startX = X_GRID[X_COMPONENT[REGC]+W_COMPONENT[REGC]];
					startY = Y_GRID[Y_COMPONENT[REGC]]+full_row_height/2;
					interX = startX+empty_col_width/3;
					interY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]+H_COMPONENT[IMM_SHIFTER]]
							+empty_row_height/2;
					interX1 = X_GRID[X_COMPONENT[MEMORY]]-empty_row_height;
					endX = X_GRID[X_COMPONENT[MEMORY]];
					endY = Y_GRID[Y_COMPONENT[MEMORY]+H_COMPONENT[MEMORY]]-full_row_height*2;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, interY);
					path.append(interX1, interY);
					path.append(interX1, endY);
					path.append(endX, endY);
					return path;
				case REGB_SHIFTER:
					startX = X_GRID[X_COMPONENT[REGB_SHIFTER]+W_COMPONENT[REGB_SHIFTER]];
					startY = Y_GRID[Y_COMPONENT[REGB_SHIFTER]]+full_row_height;
					nodeX = startX+empty_col_width/3;
					nodeY = startY;
					path = new BusPath(startX, startY);
					path.append(nodeX, nodeY);
					path.markNode(nodeX, nodeY);
					switch (cto) {
					case MUX_2PC:
						// RegBShifter.out => Mux2PC.input2
						endX = X_GRID[X_COMPONENT[MUX_2PC]];
						endY = Y_GRID[Y_COMPONENT[MUX_2PC]]+full_row_height*5/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					case MUXB:
						// RegBShifter.out => MuxB.input0
						endX = X_GRID[X_COMPONENT[MUXB]];
						endY = Y_GRID[Y_COMPONENT[MUXB]]+full_row_height/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case IMM_SHIFTER:
					// ImmShifter.out => MuxB.input2
					startX = X_GRID[X_COMPONENT[IMM_SHIFTER]+W_COMPONENT[IMM_SHIFTER]];
					startY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]]+full_row_height;
					endX = X_GRID[X_COMPONENT[MUXB]];
					endY = Y_GRID[Y_COMPONENT[MUXB]]+full_row_height*5/2;
					interX = X_GRID[X_COMPONENT[REGB_SHIFTER]]+full_col_width/3;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case MUX_2EX:
					// MuxImm2Ex.out => MuxB.input3
					startX = X_GRID[X_COMPONENT[MUX_2EX]+W_COMPONENT[MUX_2EX]];
					startY = Y_GRID[Y_COMPONENT[MUX_2EX]]+full_row_height;
					endX = X_GRID[X_COMPONENT[MUXB]];
					endY = Y_GRID[Y_COMPONENT[MUXB]]+full_row_height*7/2;
					interX = X_GRID[X_COMPONENT[REGB_SHIFTER]]+full_col_width*2/3;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case MUXA:
					// MuxA.out => ALU.input1
					startX = X_GRID[X_COMPONENT[MUXA]+W_COMPONENT[MUXA]];
					startY = Y_GRID[Y_COMPONENT[MUXA]]+full_row_height;
					endX = X_GRID[X_COMPONENT[ALU]];
					endY = Y_GRID[Y_COMPONENT[ALU]]+full_row_height*3/2;
					interX = startX+empty_col_width/2;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, endY);
					path.append(endX, endY);
					return path;
				case MUXB:
					startX = X_GRID[X_COMPONENT[MUXB]+W_COMPONENT[MUXB]];
					startY = Y_GRID[Y_COMPONENT[MUXB]]+full_row_height*5/2;
					nodeX = startX+empty_col_width/2;
					nodeY = startY;
					path = new BusPath(startX, startY);
					path.append(nodeX, nodeY);
					path.markNode(nodeX, nodeY);
					switch (cto) {
					case ALU:
						// MuxB.out => ALU.input2
						endX = X_GRID[X_COMPONENT[ALU]];
						endY = Y_GRID[Y_COMPONENT[ALU]]+full_row_height*7/2;
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					case MUX_2WD1:
						// MuxB.out => Mux2WD1.input3
						endX = X_GRID[X_COMPONENT[MUX_2WD1]];
						endY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height*7/2;
						interY = Y_GRID[Y_COMPONENT[MR]]+full_row_height/2;
						interX = endX-full_row_height/3;
						path.append(nodeX, interY);
						path.append(interX, interY);
						path.append(interX, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case ALU:
					switch (cto) {
					case ALU_OUT:
						switch (ito) {
						case ALUOut.KEY_ALU_RESULT_HI:
							// ALU.result_hi => ALUOut.result_hi
							startX = X_GRID[X_COMPONENT[ALU]+W_COMPONENT[ALU]];
							startY = Y_GRID[Y_COMPONENT[ALU]]+full_row_height*5/2+full_row_height/6;
							endX = X_GRID[X_COMPONENT[ALU_OUT]];
							endY = Y_GRID[Y_COMPONENT[ALU_OUT]]+full_row_height*2/3;
							path = new BusPath(startX, startY);
							path.append(endX, endY);
							return path;
						case ALUOut.KEY_ALU_RESULT_LO:
							startX = X_GRID[X_COMPONENT[ALU]+W_COMPONENT[ALU]];
							startY = Y_GRID[Y_COMPONENT[ALU]]+full_row_height*5/2-full_row_height/6;
							nodeX = startX+empty_col_width/2;
							nodeY = startY;
							path = new BusPath(startX, startY);
							path.append(nodeX, nodeY);
							path.markNode(nodeX, nodeY);
							// ALU.result_lo => ALUOut.result_lo
							endX = X_GRID[X_COMPONENT[ALU_OUT]];
							endY = Y_GRID[Y_COMPONENT[ALU_OUT]]+full_row_height/3;
							path.append(endX, endY);
							return path;
						}
						break;
					case MUX_2PC:
						startX = X_GRID[X_COMPONENT[ALU]+W_COMPONENT[ALU]];
						startY = Y_GRID[Y_COMPONENT[ALU]]+full_row_height*5/2-full_row_height/6;
						nodeX = startX+empty_col_width/2;
						nodeY = startY;
						// ALU.result_lo => Mux2PC.input0
						endX = X_GRID[X_COMPONENT[MUX_2PC]];
						endY = Y_GRID[Y_COMPONENT[MUX_2PC]]+full_row_height/2;
						path = new BusPath(startX, startY);
						path.append(nodeX, nodeY);
						path.markNode(nodeX, nodeY);
						path.append(nodeX, endY);
						path.append(endX, endY);
						return path;
					case FLAG_REG:
						startY = Y_GRID[Y_COMPONENT[ALU]];
						endY = Y_GRID[Y_COMPONENT[FLAG_REG]+H_COMPONENT[FLAG_REG]];
						switch (ito) {
						case FlagRegister.KEY_N:
							// ALU.N => FlagReg.N
							startX = X_GRID[X_COMPONENT[ALU]]+full_col_width/2;
							endX = X_GRID[X_COMPONENT[FLAG_REG]]+full_col_width/2;
							path = new BusPath(startX, startY);
							path.append(endX, endY);
							return path;
						case FlagRegister.KEY_Z:
							// ALU.Z => FlagReg.Z
							startX = X_GRID[X_COMPONENT[ALU]]+full_col_width*3/2;
							endX = X_GRID[X_COMPONENT[FLAG_REG]]+full_col_width*3/2;
							path = new BusPath(startX, startY);
							path.append(endX, endY);
							return path;
						case FlagRegister.KEY_C:
							// ALU.C => FlagReg.C
							startX = X_GRID[X_COMPONENT[ALU]]+full_col_width*5/2;
							endX = X_GRID[X_COMPONENT[FLAG_REG]]+full_col_width*5/2;
							path = new BusPath(startX, startY);
							path.append(endX, endY);
							return path;
						case FlagRegister.KEY_V:
							// ALU.V => FlagReg.V
							startX = X_GRID[X_COMPONENT[ALU]]+full_col_width*7/2;
							endX = X_GRID[X_COMPONENT[FLAG_REG]]+full_col_width*7/2;
							path = new BusPath(startX, startY);
							path.append(endX, endY);
							return path;
						}
						break;
					}
					break;
				case ALU_OUT:
					startX = X_GRID[X_COMPONENT[ALU_OUT]+W_COMPONENT[ALU_OUT]];
					switch (ifrom) {
					case ALUOut.KEY_ALU_OUT_LO:
						startY = Y_GRID[Y_COMPONENT[ALU_OUT]]+full_row_height/3;
						nodeX = startX+empty_col_width*2/3;
						nodeY = startY;
						path = new BusPath(startX, startY);
						path.append(nodeX, nodeY);
						path.markNode(nodeX, nodeY);
						switch (cto) {
						case MUX_2PC:
							// ALUOut.lo => Mux2PC.input2
							endX = X_GRID[X_COMPONENT[MUX_2PC]];
							endY = Y_GRID[Y_COMPONENT[MUX_2PC]]+full_row_height*3/2;
							interX = endX-empty_col_width/3;
							interY = (startY+endY)/2;
							path.append(nodeX, interY);
							path.append(interX, interY);
							path.append(interX, endY);
							path.append(endX, endY);
							return path;
						case MUX_2WD1:
							// ALUOut.lo => Mux2WD1.input0
							endX = X_GRID[X_COMPONENT[MUX_2WD1]];
							endY = Y_GRID[Y_COMPONENT[MUX_2WD1]]+full_row_height/2;
							interX = endX-full_col_width*3/4;
							interY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]+H_COMPONENT[IMM_SHIFTER]]
									+empty_row_height;
							path.append(nodeX, interY);
							path.append(interX, interY);
							path.append(interX, endY);
							path.append(endX, endY);
							path.markNode(interX, interY);
							return path;
						case MUX_PC2M:
							// ALUOut.lo => MuxPC2M.input1
							endX = X_GRID[X_COMPONENT[MUX_PC2M]];
							endY = Y_GRID[Y_COMPONENT[MUX_PC2M]]+full_row_height*3/2;
							interX = endX-empty_col_width;
							interY = Y_GRID[Y_COMPONENT[IMM_SHIFTER]+H_COMPONENT[IMM_SHIFTER]]
									+empty_row_height;
							path.append(nodeX, interY);
							path.append(interX, interY);
							path.append(interX, endY);
							path.append(endX, endY);
							return path;
						}
						break;
					case ALUOut.KEY_ALU_OUT_HI:
						startY = Y_GRID[Y_COMPONENT[ALU_OUT]]+full_row_height*2/3;
						endX = X_GRID[X_COMPONENT[REG_FILE]];
						endY = Y_GRID[Y_COMPONENT[REG_FILE]+H_COMPONENT[REG_FILE]]-full_row_height/2;
						interX = startX+empty_col_width/3;
						interY = Y_GRID[Y_COMPONENT[MR]]+full_row_height/4;
						interX1 = endX-empty_col_width/3;
						path = new BusPath(startX, startY);
						path.append(interX, startY);
						path.append(interX, interY);
						path.append(interX1, interY);
						path.append(interX1, endY);
						path.append(endX, endY);
						return path;
					}
					break;
				case FLAG_REG:
					// FlagReg.out => BranchCondGates.in
					startX = X_GRID[X_COMPONENT[FLAG_REG]+W_COMPONENT[FLAG_REG]];
					startY = Y_GRID[Y_COMPONENT[FLAG_REG]]+full_row_height/2;
					endX = X_GRID[X_COMPONENT[BRANCH_COND_GATES]];
					endY = Y_GRID[Y_COMPONENT[BRANCH_COND_GATES]]+full_row_height;
					interY = Y_GRID[Y_COMPONENT[CONTROL]]-empty_row_height/2;
					interX = startX+empty_col_width/4;
					interX1 = endX-empty_col_width/4;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, interY);
					path.append(interX1, interY);
					path.append(interX1, endY);
					path.append(endX, endY);
					return path;
				case MUX_2PC:
					// Mux2PC.out => PC.in
					startX = X_GRID[X_COMPONENT[MUX_2PC]+W_COMPONENT[MUX_2PC]];
					startY = Y_GRID[Y_COMPONENT[MUX_2PC]]+full_row_height*3/2;
					endX = X_GRID[X_COMPONENT[PC]];
					endY = Y_GRID[Y_COMPONENT[PC]]+full_row_height/2;
					interX = startX + empty_col_width/4;
					interX1 = endX-empty_col_width/2;
					interY = Y_GRID[Y_COMPONENT[CONTROL]]-empty_row_height;
					path = new BusPath(startX, startY);
					path.append(interX, startY);
					path.append(interX, interY);
					path.append(interX1, interY);
					path.append(interX1, endY);
					path.append(endX, endY);
					return path;
				}
				return null;
		}
	}
	
	private static class Point {
		int x, y;
		Point(int x, int y) {
			this.x = x;
			this.y = y;
		}
	}
	
	private static class BusPath {
		List<Point> points;
		List<Point> nodes;
		private int length = -1;
		private List<Integer> len;
		
		BusPath(int x, int y) {
			points = new ArrayList<Point>();
			nodes = new ArrayList<Point>();
			points.add(new Point(x, y));
		}
		void append(int x, int y) {
			points.add(new Point(x, y));
		}
		void markNode(int x, int y) {
			nodes.add(new Point(x, y));
		}
		
		@Override
		public String toString() {
			StringBuffer buf = new StringBuffer();
			for (Point p : points) {
				buf.append("("+p.x+","+p.y+"),");
			}
			return buf.toString();
		}
		
		BusPath getProgressed(int progress) {
			BusPath prog = new BusPath(points.get(0).x, points.get(0).y);
			if (length < 0) {
				len = new ArrayList<Integer>();
				for (int i = 0; i < points.size()-1; i++) {
					Point p1 = points.get(i);
					Point p2 = points.get(i+1);
					int l = 0;
					if (p1.x == p2.x) {
						l = Math.abs(p1.y-p2.y);
					} else {
						l = Math.abs(p1.x-p2.x);
					}
					len.add(l);
					length += l;
				}
			}
			int allowedLen = length*progress/100;
			int alreadyLen = 0;
			for (int i = 0; i < len.size(); i++) {
				Point p1 = points.get(i);
				Point p2 = points.get(i+1);
				if (alreadyLen + len.get(i) <= allowedLen) {
					prog.append(p2.x, p2.y);
				} else {
					int dif = allowedLen-alreadyLen;
					if (p1.x == p2.x && p1.y > p2.y) { // up
						prog.append(p1.x, p1.y-dif);
					} else if (p1.x == p2.x && p1.y < p2.y) { // down
						prog.append(p1.x, p1.y+dif);
					} else if (p1.y == p2.y && p1.x < p2.x) { // right
						prog.append(p1.x+dif, p1.y);
					} else if (p1.y == p2.y && p1.x > p2.x) { // left
						prog.append(p1.x-dif, p1.y);
					}
					break;
				}
			}
			return prog;
		}
	}
}